package com.usretyn.game;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

import static com.usretyn.game.Constants.BOX_SIZE;
import static com.usretyn.game.Constants.NUMBER_HOLDER;


/**
 * @author sunjf
 * @since 2021/8/19 16:06
 */
public final class SudokuUtils {
    private static Logger logger = LoggerFactory.getLogger(SudokuUtils.class);
    private SudokuUtils() {

    }
    public static final String ANGLE = "+";
    public static final String EDGE= "-";
    public static void check(int index){
        if(index < 0 || index > Constants.SQUARE_SIZE-1) {
            throw new IllegalArgumentException("index must between 0~8 ");
        }
    }

    public static void check(int row,int col){
        check(row);
        check(col);
    }

    public static List<int[][]> calculate(int[][] matrix){
        List<int[][]> answers = new ArrayList<>();
        Map<Coordinate,List<Integer>> coordinates  = getFillCoordinates(matrix);
        List<Coordinate> toFillCoordinateList = new ArrayList<>(coordinates.keySet()) ;

        toFillCoordinateList.sort(Comparator.comparingInt(o -> coordinates.get(o).size()));
        if(toFillCoordinateList.isEmpty()){
            answers.add(matrix);
            return answers;
        }

        calculate(matrix,answers,toFillCoordinateList,coordinates,0);
        return answers;
    }

    private static void calculate(int[][] matrix,List<int[][]> answers,List<Coordinate> toFillCoordinateList,Map<Coordinate,List<Integer>> coordinates,int index){
        int size = toFillCoordinateList.size();
        if(index==size ){
            return;
        }
        Coordinate coordinate = toFillCoordinateList.get(index);
        List<Integer> values =  coordinates.get(coordinate);

        if(values.isEmpty()){
            logger.debug("coordinate {} ,index {}",coordinate,index);
            return;
        }
        int[][] clone = cloneArray(matrix);
        for (Integer integer : values) {
            clone[coordinate.getRow()][coordinate.getCol()] = integer;
            if(integer == size -1){
                answers.add(clone);
            }
            calculate(clone,answers,toFillCoordinateList,coordinates,index+1);
        }

    }



    public static Map<Coordinate,List<Integer>> getFillCoordinates(int[][] matrix){
        Map<Coordinate,List<Integer>> coordinates = new HashMap<>(32);
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[i].length; j++) {
                if (matrix[i][j]== NUMBER_HOLDER){

                    coordinates.put(new Coordinate(i,j),candidates(i,j,matrix));
                }
            }
        }
        return coordinates;
    }


    public static int[][] cloneArray(int[][] matrix){
        int[][] result = new int[matrix.length][];
        for (int i = 0; i < matrix.length; i++) {
            result[i] = new int[matrix[i].length];
            System.arraycopy(matrix[i],0,result[i],0,matrix[i].length);
        }
        return result;
    }

    public static List<Integer> candidates(int row, int col, int[][] matrix){
        check(row,col);
        if(matrix[row][col] != Constants.NUMBER_HOLDER){
            throw new IllegalStateException(String.format("row[%d]column[%d] has fixed value %d",row,col,matrix[row][col]));
        }
        Grid grid = new Grid(row,col);
        ArrayList<Integer> possibleValues = new ArrayList<>(Constants.POSSIBLE_VALES);
        for (int i = 0; i < Constants.SQUARE_SIZE; i++) {
            int value = matrix[row][i];
            if(value != Constants.NUMBER_HOLDER){
                possibleValues.remove(Integer.valueOf(value));
                if(possibleValues.isEmpty()){
                    return possibleValues;
                }
            }
        }
        for (int i = 0; i < Constants.SQUARE_SIZE; i++) {
            int value = matrix[i][col];
            if(value != Constants.NUMBER_HOLDER){
                possibleValues.remove(Integer.valueOf(value));
                if(possibleValues.isEmpty()){
                    return possibleValues;
                }
            }
        }
        for (int i=grid.getBoxMinRow();i<=grid.getBoxMaxRow();i++){
            for (int j = grid.getBoxMinCol(); j <= grid.getBoxMaxCol(); j++){
                int value = matrix[row][i];
                if(value != Constants.NUMBER_HOLDER){
                    possibleValues.remove(Integer.valueOf(value));
                    if(possibleValues.isEmpty()){
                        return possibleValues;
                    }
                }
            }
        }
        return possibleValues;
    }

    public static void print(int[][] matrix){
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < matrix.length; i++) {
            if(i% BOX_SIZE ==0){
                builder.append("+-------+-------+-------+").append("\n");
            }
            for (int j = 0; j < matrix[i].length; j++) {
                if(j ==0){
                    builder.append("|");
                }
                int value = matrix[i][j];
                if(value != NUMBER_HOLDER){
                    builder.append(" ").append(value);
                }else{
                    builder.append(" ").append(".");
                }
                if(j% BOX_SIZE ==(BOX_SIZE-1)){
                    builder.append(" |");
                }
            }
            builder.append("\n");
        }
        builder.append("+-------+-------+-------+").append("\n");
        System.out.println(builder.toString());
    }
}
